kitt_score 0.1.0

Decision engine at the core of Project KITT — in-memory stateful matching with pluggable scoring backends.
Documentation
# Predicate Bytecode

This note covers `src/scoring/backends/predicate/*`.

## End-to-end pipeline

The predicate backend is implemented as a compiler plus VM.

1. source text enters as `ScorerSpec::Predicate("...")`,
2. [[#Parser]] turns text into [[#Expr]],
3. `typecheck.rs` resolves names against [[Modules/Schema#Schema]] and emits [[#Op]] values,
4. those ops are stored in a [[#Program]],
5. `vm.rs` executes the program against a [[Modules/Location State#LocationView]].

## `Expr`

**Defined in:** `ast.rs`

The AST enum for predicate expressions.

### Variants
- `Num(f64)`
- `Slot { kind: String, attr: String }`
- `Neg(Box<Expr>)`
- `Not(Box<Expr>)`
- `Bin(BinOp, Box<Expr>, Box<Expr>)`
- `Call(String, Vec<Expr>)`

### Role
This is the purely syntactic representation before slot references become byte offsets.

## `BinOp`

**Defined in:** `ast.rs`

The binary operator enum.

### Variants
- `Add`
- `Sub`
- `Mul`
- `Div`
- `Lt`
- `Le`
- `Gt`
- `Ge`
- `Eq`
- `Ne`
- `And`
- `Or`

## `Parser<'a>`

**Defined in:** `parser.rs`

A handwritten recursive-descent parser over the predicate DSL.

### Public API
- `Parser::new(src)`
- `parse()`

### Why handwritten parsing is reasonable here
The language is small and expression-shaped. A custom parser keeps the grammar readable and avoids parser-framework overhead.

### Supported constructs
- numeric literals,
- slot refs like `$audience.male_frac`,
- arithmetic,
- comparisons,
- logical operations,
- builtin calls such as `min`, `max`, `abs`.

## `Op`

**Defined in:** `bytecode.rs`

The VM opcode enum.

### Literal and load ops
- `PushF32(f32)`
- `LoadI64 { offset }`
- `LoadF32 { offset }`
- `LoadF64 { offset }`
- `LoadU32 { offset }`

### Unary ops
- `Neg`
- `Not`
- `Abs`

### Binary arithmetic ops
- `Add`
- `Sub`
- `Mul`
- `Div`

### Comparison ops
- `Lt`
- `Le`
- `Gt`
- `Ge`
- `Eq`
- `Ne`

### Logical ops
- `And`
- `Or`

### Aggregate-style ops
- `MinA`
- `MaxA`

### Semantics
The VM stack is `f32` only. Booleans are represented as `0.0` and `1.0`.

## `Program`

**Defined in:** `bytecode.rs`

The compiled executable predicate.

### Fields
- `ops: Vec<Op>`
- `max_stack: usize`

### Role
`max_stack` lets the VM pre-size its stack with minimal overhead.

## `PredicateBuilder`

**Defined in:** `mod.rs`

The backend-specific implementation of [[Modules/Scoring Core#ScorerBuilder]].

### Role
Validates that the incoming [[Modules/Scoring Core#ScorerSpec]] is a predicate spec, then performs parse + compile.

## `PredicateScorer`

**Defined in:** `mod.rs`

A small wrapper around a compiled [[#Program]].

### Role
Implements the [[Modules/Scoring Core#Scorer]] trait by delegating to the VM.

## Compilation semantics in `typecheck.rs`

Although the file is named `typecheck.rs`, it performs both validation and code generation.

### What it validates
- kind names exist,
- attribute names exist,
- resolved slots are of supported scalar types,
- builtin names and arities are valid.

### What it rejects
Array-valued slots are rejected for predicate scoring in the current implementation.

### What it emits
A linear sequence of [[#Op]] values plus stack-depth bookkeeping.

## VM semantics in `vm.rs`

### Stack representation
`SmallVec<[f32; 16]>`

### Important behaviors
- division by zero yields `0.0`,
- unbalanced programs yield `0.0` instead of panicking,
- `Load*` instructions read by raw offset from [[Modules/Location State#LocationView]].

### Critical architectural fact
The VM never resolves names at runtime. The schema lookup cost has already been paid at compile time.

## Why this subsystem matters

This backend is the best illustration of the crate's design philosophy:
- low-level storage,
- explicit compilation,
- predictable execution,
- minimal trigger-time branching.

It is also the subsystem used in the audience-matching scenario documented in [[Scenarios/Audience Bytecode Matching]].